﻿using FDSimpleModelGenerator.Common;
using FDSimpleModelGenerator.Entities.Attributes;
using FDSimpleModelGenerator.IRepositories;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
using System.Text;

namespace FDSimpleModelGenerator.Repositories
{
    /// <summary>
    /// 生成Sql增删改除语句
    /// </summary>
    public class GenerateDbSql : IGenerateDbSql
    {
        private readonly ICache _cache;
        private readonly TimeSpan _cacheSpan = TimeSpan.FromMinutes(30);

        public GenerateDbSql(ICache cache)
        {
            _cache = cache;
        }

        /// <summary>
        /// 获取类型列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type">操作类型</param>
        /// <param name="ignoreColumns">指定忽略的列</param>
        /// <returns></returns>
        private IList<string> GetColumns<T>(ColumnType type, IList<string> ignoreColumns = null)
        {
            return _cache.Get<IList<string>>($"Cache_{type}_GetColumns_{typeof(T).FullName}_{string.Join("_", ignoreColumns ?? new List<string>())}", () =>
            {
                var props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
                var columns = new List<string>();
                foreach (var prop in props)
                {
                    var ignoreUpdate = type == ColumnType.Update && prop.GetCustomAttribute<IgnoreUpdateColumnAttribute>() != null;
                    var ignoreInsert = type == ColumnType.Insert && prop.GetCustomAttribute<IgnoreInsertColumnAttribute>() != null;
                    var ignoreColumn = prop.GetCustomAttribute<IgnoreColumnAttribute>() != null; //s().OfType<IgnoreColumnAttribute>();
                    var name = prop.GetCustomAttribute<ColumnAttribute>()?.Name ?? prop.Name;
                    if (ignoreInsert || ignoreUpdate || ignoreColumn ||
                        (ignoreColumns?.Any(c => c.Equals(name, StringComparison.OrdinalIgnoreCase)) ?? false))
                    {
                        continue;
                    }
                    columns.Add(name);
                }
                return columns;
            }, TimeSpan.FromHours(2));
        }

        /// <summary>
        /// 表名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private string TableName<T>()
        {
            return typeof(T).GetCustomAttribute<TableAttribute>()?.Name ?? typeof(T).Name;
        }

        public string GetDeleteString<T>(string condition)
        {
            return _cache.Get<string>($"Cache_GetDeleteString_{typeof(T).FullName}_{condition}", () =>
            {
                var sql = $"DELETE FROM {TableName<T>()} WHERE {condition}";
                return sql;
            }, _cacheSpan);
        }

        public string GetInsertString<T>(params string[] ignoreColumns)
        {
            return _cache.Get($"Cache_GetInsertString_{typeof(T).FullName}_{string.Join('_', ignoreColumns)}", () =>
            {
                var sqlInsert = new StringBuilder();
                var sqlValue = new StringBuilder();
                sqlInsert.Append($"INSERT INTO {TableName<T>()}(");
                sqlValue.Append(" VALUES(");
                if (!ignoreColumns.Any(c => c.Equals("id", StringComparison.OrdinalIgnoreCase)))
                {
                    ignoreColumns = ignoreColumns.Append("id").ToArray();
                }
                foreach (var column in GetColumns<T>(ColumnType.Insert, ignoreColumns))
                {
                    sqlInsert.Append($"{column},");
                    sqlValue.Append($"@{column},");
                }
                sqlInsert.Remove(sqlInsert.Length - 1, 1);
                sqlValue.Remove(sqlValue.Length - 1, 1);
                sqlValue.Append(")");
                sqlInsert.Append(")").Append(sqlValue);
                return sqlInsert.ToString();
            }, _cacheSpan);
        }

        public string GetQueryString<T>(string condition, params string[] specifiedColums)
        {
            return _cache.Get($"Cache_GetQueryString_{typeof(T).FullName}_{condition}_{string.Join('_', specifiedColums)}", () =>
            {
                var sql = new StringBuilder();
                sql.Append("SELECT ");
                Action<string[]> addColumns = columns =>
                 {
                     foreach (var column in columns)
                     {
                         sql.Append($"{column},");
                     }
                     sql.Remove(sql.Length - 1, 1);
                 };
                if (specifiedColums.Length > 0)
                {
                    addColumns(specifiedColums);
                }
                else
                {
                    addColumns(GetColumns<T>(ColumnType.Query, null).ToArray());
                }
                sql.Append($" FROM {TableName<T>()} ");
                AppendWhere(sql, condition);
                return sql.ToString();
            }, _cacheSpan);
        }

        public string GetUpdateString<T>(string condition, params string[] ignoreColumns)
        {
            return _cache.Get($"Cache_GetUpdateString_{typeof(T).FullName}_{condition}_{string.Join('_', ignoreColumns)}", () =>
            {
                var sql = new StringBuilder();
                sql.Append($"UPDATE {TableName<T>()} SET ");
                foreach (var column in GetColumns<T>(ColumnType.Update, ignoreColumns))
                {
                    sql.Append($"{column}=@{column},");
                }
                sql.Remove(sql.Length - 1, 1);
                AppendWhere(sql, condition);
                return sql.ToString();
            }, _cacheSpan);
        }

        private void AppendWhere(StringBuilder sql, string condition)
        {
            if (!condition.IsNullOrEmpty() && !condition.Trim().StartsWith("where", StringComparison.OrdinalIgnoreCase))
            {
                sql.Append(" WHERE ");
            }
            sql.Append(condition);
        }

        private enum ColumnType
        {
            Insert,
            Query,
            Delete,
            Update
        }
    }
}
